var urls = {
    LOGIN_C_URL: "https://html.m.cmbchina.com/MobileHtml/Login/LoginC.aspx",
    CAPTHCA_URL: "https://html.m.cmbchina.com/MobileHtml/Login/ExtraPwd.aspx?ClientNo=%s&random=%s",
    SEND_SMS_URL: "https://html.m.cmbchina.com/MobileHtml/Login/MsgVerify.aspx",
    SUBMIT_SMS_URL: "https://html.m.cmbchina.com/MobileHtml/Login/MsgVerify.aspx",
    ACOUNT_URL: "https://html.m.cmbchina.com/MobileHtml/creditcard/m_accountmanage/am_queryaccount.aspx",
    BILL_LIST_URL: "https://html.m.cmbchina.com/MobileHtml/creditcard/m_accountmanage/am_queryreckoningsurvey.aspx",
    BILL_URL: "https://html.m.cmbchina.com/MobileHtml/CreditCard/M_AccountManage/am_QueryReckoningList.aspx",
    WEI_OUT_URL: "https://html.m.cmbchina.com/MobileHtml/creditcard/m_accountmanage/am_queryreckoningsurveynot.aspx",
    WEI_OUT_DETAIL_URL: "https://html.m.cmbchina.com/MobileHtml/CreditCard/M_AccountManage/am_QueryReckoningSurveyNot.aspx"
}
var params = {
    USER_AGENT: "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36",
    ACCEPT: "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    //身份证参数
    CERT_XML_REQ: "<LoginMode>0</LoginMode>"
    + "<LoginByCook>false</LoginByCook>"
    + "<IDTypeC>01</IDTypeC>"
    + "<IDNoC>%s</IDNoC>"
    + "<PwdC>%s</PwdC>"
    + "<ExtraPwdC>%s</ExtraPwdC>"
    + "<RememberFlag>false</RememberFlag>"                                                                                 //Chrome/58.0.3029.110 Mobile Safari/537.36
    + "<UserAgent>Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Mobile Safari/537.36</UserAgent>"
    + "<screenW>444</screenW>"//400
    + "<screenH>252</screenH>"//409
    + "<OS>Win32</OS>",
    //卡号参数
    CREDITCARDNO: "<CreditCardNo>%s</CreditCardNo>"
    + "<PwdC>%s</PwdC>"
    + "<ExtraPwdC>%s</ExtraPwdC>"
    + "<LoginMode>1</LoginMode>"
    + "<LoginByCook>false</LoginByCook>"
    + "<RememberFlag>false</RememberFlag>"
    + "<UserAgent>Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Mobile Safari/537.36</UserAgent>"
    + "<screenW>400</screenW>"
    + "<screenH>550</screenH>"
    + "<OS>Win32</OS>"


}

// var getFenQi = Pattern.compile("\\d+");//[\\d.]+-[\\d.]+
// 如果页面含有“退出”，说明登录成功
var SUCCESS_TAG = "退出";
// 如果页面含有“短信验证”，说明需要短信验证码
var SMS_TAG = "短信验证";
// 提交请求的时候需要用，
var clientNo = "";
// 是否需要短信验证码
var needSmsFlag = false;
// 账单日（不包含年月）
var billDate = "";
// 还款日（不包含年月）
var repaymentDate = "";
// 完整卡号
var cardNo = "";
//账单月
var billMonth = "";
//还款月
var payMonth = "";
var contextMap = new HashMap();
var remark_regx = /[\s\S]*\d+-\d+/
var needReLogin = true;


var v4_cmb_mobile_credit_java_funcs = {
    openHomePage : function(){
        logger.info("任务【{}】,打开首页",context.get(LUA_TASK_KEY));
        var indexHtml = cmbSubmitWithReTry(urls.LOGIN_C_URL,null,null,"获取登录页面","get");
        storeDataToFile(context, indexHtml, "loginPage.html")
        var doc = Jsoup.parse(indexHtml)
        var captchaUrl = doc.select("input[type='image']").attr("src")
        context.put("captchaUrl", captchaUrl)
        if(StringUtils.isNotBlank(captchaUrl) && captchaUrl.split("\?").length>=2 && captchaUrl.split("\?")[1].split("&").length>=1 && captchaUrl.split("\?")[1].split("&")[0].split("=").length>=2){
            clientNo = captchaUrl.split("\?")[1].split("&")[0].split("=")[1]
        }
    },
    checkProxyAvailable: function () {
        var captchaUrl = context.get("captchaUrl")
        return StringUtils.isNotBlank(captchaUrl)
    },
    checkTargetWebsiteAvailable: function(){
        //将成功获取到ClientNo定义为目标网站可用
        return StringUtils.isNotBlank(clientNo)
    },
    needResetEnvironment : function() {
        return equals(context.get("currentErrorCode"),BALO_99602_50) || equals(context.get("currentErrorCode"),BALO_22006_10);
    },
    checkControlAvailable: function () { return true; },

    needRefreshHomePageOrNot : function() {
        return true;
    },
    needResetEnvironment:function(){
        getProxyInfo(context);
        return true;
    },

    login:function () {
        if(needReLogin){
            logger.info("任务【{}】,招商银行信用卡开始登录",context.get(LUA_TASK_KEY));
            //获取图片验证码和登录参数
            logger.info("任务【{}】,招商银行开始进行登录参数获取，并进行登录参数拼装",context.get(LUA_TASK_KEY));
            var xmlReq = null;
            var loginNoTmp = getLoginParams(context)[0];
            if (equals(IDCARD, LoginType.stringToLoginType(context.get(LUA_TASK_LOGIN_TYPE)))) {//身份证
                xmlReq = params.CERT_XML_REQ;
            } else if (equals(CREDITCARDNO, LoginType.stringToLoginType(context.get(LUA_TASK_LOGIN_TYPE)))) {//卡号
                xmlReq = params.CREDITCARDNO;
            }
            var password = context.get(LUA_TASK_TOKEN);
            if (password.length() > 6) {
                logger.info("{},密码的前六位:{}", context.get(LUA_TASK_KEY), password.substring(0, 6));
            }
            logger.info("任务【{}】，开始获取图片验证码",context.get(LUA_TASK_KEY));
            var random = new Random();
            context.put(Constants.TASK_IMG_BYTES, RequestUtil.getAsBytes(format(urls.CAPTHCA_URL, clientNo, random.nextFloat())));
            context.put(TASK_IMG_REGEX,"[0-9]{4}");
            var captchaStr = waitImgCodeBySecondsByJs(context,60,true,3);
            logger.info("任务【{}】,获取到的图片验证码为：【{}】",context.get(LUA_TASK_KEY),captchaStr);
            xmlReq = String.format(xmlReq, loginNoTmp, password, captchaStr);
            context.put("xmlReq",xmlReq);
            logger.info("任务【{}】,拼装后的登录参数为：【{}】",context.get(LUA_TASK_KEY),xmlReq);

            //开始发送登录请求
            var loginHtml = getLoginHtml();
            logger.info("任务【{}】，登录结果：【{}】",context.get(LUA_TASK_KEY),loginHtml)
            //校验登录的流程是否正确，设置是否需要短信验证码的标志
            var isLoginCorrectFunc = function(loginHtml){
                if(StringUtils.contains(loginHtml, SUCCESS_TAG)) {
                    context.put("needSms", false)
                    return true
                } else if(StringUtils.contains(loginHtml, SMS_TAG)){
                    context.put("needSms", true)
                    return true
                } else {
                    return false
                }

            }
            //获取错误信息
            var getLoginErrorMsg = function(loginHtml){
                if(loginHtml.contains("为了您的帐户安全，设置密码时请不要选用您的身份证、生日、手机号码、卡号、重复或连续等简单的数字。")){
                    return "为了您的帐户安全，设置密码时请不要选用您的身份证、生日、手机号码、卡号、重复或连续等简单的数字。"
                } else if(loginHtml.contains("无效附加码#2")){
                    return "图片验证码错误"
                } else if(loginHtml.contains("亲爱的准信用卡客户，请您登录掌上生活客户端进行相关业务的查询与办理")){
                    return "亲爱的准信用卡客户，请您登录掌上生活客户端进行相关业务的查询与办理";
                }else if(loginHtml.contains("信用卡开卡")){
                    return "信用卡开卡"
                }else if(loginHtml.contains("由于您的请求正在处理中，请不要频繁操作，稍候片刻再返回菜单继续使用。")){
                    return "频繁操作"
                }else if(RegexUtils.GetText(loginHtml, "errMsg = \"(.*)\";").contains("不能在当前渠道使用，如需使用，请登录“网上银行专业版”在“专业版管理”中重新开通使用。")){
                    return "不能在当前渠道使用，如需使用，请登录“网上银行专业版”在“专业版管理”中重新开通使用。"
                } else if(StringUtils.contains(loginHtml, "Service Unavailable")){
                    //Service Unavailable
                    return "系统繁忙，请稍后再试。"
                } else {
                    return RegexUtils.GetText(loginHtml, "errMsg = \"(.*)\";")
                }

            }
            assertTrueOrThrowException(loginHtml, isLoginCorrectFunc, getLoginErrorMsg)

            //短信验证码操作
            if(context.get("needSms")){
                logger.info("任务【{}】，需要短信验证功码，开始发送",context.get(LUA_TASK_KEY));
                //发送短信验证码
                var parametersMap = new HashMap();
                parametersMap.put("ClientNo", clientNo);
                parametersMap.put("MsgVerifyCmd", "Ajax_MSG_SEND");
                parametersMap.put("MsgFlag_", "010000");
                parametersMap.put("ddlMobile", "1");
                parametersMap.put("$RequestMode$", "1");
                parametersMap.put("MsgCode", "");
                parametersMap.put("Command", "");
                parametersMap.put("MsgTips_", "请选择一个手机号码，并点击'发送短信验证码'按钮来发送验证码。如果你已经取得验证码，请直接在下面的文本框中输入即可。");
                var smsHeader = new HashMap();
                smsHeader.put("User-Agent", params.USER_AGENT);
                smsHeader.put("Accept", "*/*");
                smsHeader.put("Host", "html.m.cmbchina.com");
                smsHeader.put("Origin", "https://html.m.cmbchina.com");
                smsHeader.put("Referer", "https://html.m.cmbchina.com/MobileHtml/Login/LoginC.aspx");
                smsHeader.put("X-Requested-With", "XMLHttpRequest");
                smsHeader.put("Accept-Encoding", "gzip, deflate, br");
                smsHeader.put("Connection", "keep-alive");
                smsHeader.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
                var html = post(urls.SEND_SMS_URL, parametersMap, smsHeader);
                storeDataToFile(context, html, "smsSendResult.json")
                logger.info("任务【{}】，短信验证码发送结果【{}】",context.get(LUA_TASK_KEY),html);

                //check开始: 检查短信是否发送成功或是否是短信过于频繁
                var isSendSmsCorrectFunc = function(html) {
                    var content = JSON.parse(html).get("$SysResult$").get("$Content$")
                    var msg = JSON.parse(content)
                    return msg !== null
                        && msg.get("DispMsg").contains("短信验证码已发送至手机")
                        && msg.get("DispMsg").contains("请在5分钟内完成验证")
                }

                var getSendSmsErrorMsgFunc = function(html) {
                    var content = JSON.parse(html).get("$SysResult$").get("$Content$")
                    var sysCode = JSON.parse(html).get("$SysResult$").get("$SysCode$")
                    var msg = JSON.parse(content)
                    if(msg !== null){
                        if(msg.get("DispMsg").contains("您在24小时内验证码申请次数超过20次")){
                            return "您在24小时内验证码申请次数超过20次,请明天再试。";
                        }else if("406".equals(sysCode)){
                            return "频繁操作"
                        }else{
                            return msg.get("DispMsg")
                        }
                    } else {
                        return ""
                    }
                }
                assertTrueOrThrowException(html, isSendSmsCorrectFunc, getSendSmsErrorMsgFunc)
            }
        }

        var startTime = System.currentTimeMillis();
        if(context.get("needSms")){
            // 获取短信验证码
            var waitstartTime = System.currentTimeMillis();
            var verifyMessage = waitSmsCodeBySeconds(context, 120);
            var waitEndTime = System.currentTimeMillis();
            logger.info("任务【{}】，等待用户短信时长【{}】",context.get(LUA_TASK_KEY),waitEndTime-waitstartTime)

            startTime = System.currentTimeMillis();
            logger.info("任务【{}】，收到用户短信，当前时间戳【{}】",context.get(LUA_TASK_KEY),startTime)
            logger.info("任务【{}】，用户输入的短信验证码【{}】",context.get(LUA_TASK_KEY),verifyMessage);
            //check开始: 检查短信是否发送成功或是否是短信过于频繁
            var isSmsCodeCorrectFunc = function(verifyMessage) {
                return StringUtils.isNotBlank(verifyMessage) && verifyMessage.length === 6
            }
            var getSmsCodeErrorMsgFunc = function(verifyMessage) {
                return "短信验证码错误"
            }
            assertTrueOrThrowException(verifyMessage, isSmsCodeCorrectFunc, getSmsCodeErrorMsgFunc)

            logger.info("任务【{}】，开始进行最终的登录提交",context.get(LUA_TASK_KEY));
            parametersMap = new HashMap();
            parametersMap.put("ClientNo", clientNo);
            parametersMap.put("MsgFlag_", "110000");
            parametersMap.put("MsgCode", verifyMessage);
            parametersMap.put("ddlMobile", "1");
            parametersMap.put("Command", "CMD_VERIFY");
            parametersMap.put("DeviceTAB", "TabDefault");

            var receiveSmsHeader = new HashMap();
            receiveSmsHeader.put("User-Agent", params.USER_AGENT);
            receiveSmsHeader.put("Accept", params.ACCEPT);
            receiveSmsHeader.put("Host", "html.m.cmbchina.com");
            receiveSmsHeader.put("Origin", "https://html.m.cmbchina.com");
            receiveSmsHeader.put("Referer", "https://html.m.cmbchina.com/MobileHtml/Login/LoginC.aspx");

            //html = post(urls.SUBMIT_SMS_URL, parametersMap, receiveSmsHeader);
            html = cmbSubmitWithReTry(urls.SUBMIT_SMS_URL,parametersMap,receiveSmsHeader,"输入短信验证码","post");
            storeDataToFile(context, html, "loginSuccess.html")
            //html = "验证验证码失败，原因：请重新申请验证码。"
            //判断是否登录成功
            var isSendSmsCorrectFunc = function(html){
                return StringUtils.contains(html, SUCCESS_TAG)
            }
            var getSendSmsMsgFunc = function(html) {
                if(html.contains("请输入正确的验证码或重新申请验证码") || html.contains("验证验证码失败，原因：验证码错误，请重新输入")){
                    needReLogin = false;
                }else if(html.contains("验证码已过期，请重新申请验证码") || html.contains("验证验证码失败，原因：请重新申请验证码") || html.contains("验证错误次数已超过3次，请重新申请验证码")){
                    v4_cmb_mobile_credit_java_funcs.openHomePage();//重新打开下首页
                    needReLogin = true;
                }
                if(html.contains("由于您的请求正在处理中，请不要频繁操作，稍候片刻再返回菜单继续使用。") || html.contains("请重新登录") || html.contains("操作失败!")){
                    return "频繁操作"
                }
                return Jsoup.parse(html).select("#TitleMsg").text()
                //return "验证验证码失败，原因：请重新申请验证码。"
            }
            assertTrueOrThrowException(html, isSendSmsCorrectFunc, getSendSmsMsgFunc);
        }
        logger.info("任务【{}】，用户登录结束，从收到短信到登录结束用时【{}】",context.get(LUA_TASK_KEY),(System.currentTimeMillis() - startTime))
    },
    crawl: function () {
        var creditCardAllData = new CreditCardAllData();
        var bills = new ArrayList();
        creditCardAllData.setCardId(UUID.randomUUID().toString());
        creditCardAllData.setBankName("招商银行");
        creditCardAllData.setCardType("信用卡");
        var cardNum = 1;
        // 抓取卡片
        crawlCardInfo(creditCardAllData, cardNum);
        // 抓取账户
        crawlAcountInfo(creditCardAllData, cardNum);
        var billAllData = new BillAllData();
        // 抓取账单
        crawlBillsInfo(bills, creditCardAllData, cardNum);
        // 未出账单
        crawlBillOut(bills, creditCardAllData, cardNum, billAllData);
        bills.add(billAllData);

        var creditCardTimeDeposit = new ArrayList();
        creditCardAllData.setDeposits(creditCardTimeDeposit);
        creditCardAllData.setBills(bills);
        var creditCardAllDatas = new ArrayList();
        creditCardAllDatas.add(creditCardAllData)
        context.put(CREDIT_DATA, creditCardAllDatas);

    },
    exitLogin: function () {}

}

/**
 * 抓取卡片
 */
function crawlCardInfo(creditCardAllData, cardNum) {
    try {
        logger.info("招商银行抓取抓取卡片信息，包含卡号，用户名");
        var baseURL = "https://html.m.cmbchina.com/MobileHtml/creditcard/m_cardmanage/cm_queryenforcecard.aspx";
        var parametersMap = new HashMap();
        parametersMap.put("ClientNo", clientNo);
        parametersMap.put("Command", "");
        parametersMap.put("XmlReq", "");
        var html = cmbSubmitWithReTry(baseURL,parametersMap,accountAndcardNoAndbilloutAndbillHeaders(),"卡信息","post");
        storeDataToFile(context, html, cardNum + "_card.html");
        var doc = Jsoup.parse(html);
        var ps = doc.select(".ui-tb-l p:first-child");
        //是否开卡
        var isOpen = doc.select("div:nth-child(2) > label:nth-child(1)");
        var full_card_numList = new ArrayList();
        var username = "";
        for (var i = 0; i < isOpen.size(); i++) {
            if (!equals("未开卡",isOpen.get(i).text().trim())) {
                cardNo = ps.get(i).text().substring(0, ps.get(i).text().indexOf("（"));
                username = RegexUtils.GetText(ps.get(i).text(), "（(.*)）");
                full_card_numList.add(cardNo);
            }
        }

        logger.info("taskKey={} 银行卡数量为：{}", context.get(LUA_TASK_KEY), full_card_numList.size());
        creditCardAllData.setFullCardNum(list2String(full_card_numList));
        creditCardAllData.setCardNum(getCardNumList(full_card_numList));
        creditCardAllData.setNameOnCard(username);
        creditCardAllData.setOpenBank("");

    } catch (e) {
        logger.error("taskKey={} 卡信息抓取异常：{}", context.get(LUA_TASK_KEY), e);
        statCaughtException(context, e);
    }
}

/**
 * 抓取账户
 */
function crawlAcountInfo(creditCardAllData, cardNum) {
    logger.info("招商银行抓取账户信息，包含额度、账单日");
    try {
        var parametersMap = new HashMap();
        parametersMap.put("ClientNo", clientNo);
        parametersMap.put("XmlReq", "");
        parametersMap.put("Command", "");
        var html = cmbSubmitWithReTry(urls.ACOUNT_URL,parametersMap,accountAndcardNoAndbilloutAndbillHeaders(),"账户信息","post");
        storeDataToFile(context, html, cardNum + "_card_acount.html");
        var doc = Jsoup.parse(html);

        // 可用信用额度 #cphBody_LimitAvail > a > span
        var availableLimit = doc.select("#cphBody_LimitAvail > a > span").text().trim().replaceAll("￥", "").replaceAll(",",
            "");
        var availableLimitBD = new BigDecimal(availableLimit);

        // 信用额度 #cphBody_CreditLimit > a > span
        var creditLimit = doc.select("#cphBody_CreditLimit > a > span").text().trim().replaceAll("￥", "").replaceAll(",",
            "");

        // 可用取现额度 #cphBody_PreCashLimit > a > span
        var enchashmentImprest = doc.select("#cphBody_PreCashLimit > a > span").text().trim().replaceAll("￥", "")
            .replaceAll(",", "");

        // 还款日（不包含年月） #cphBody_BQDQHQDate > a > span
        var repaymentDateTmp = doc.select("#cphBody_BQDQHQDate > a > span").text().trim();

        var repaymentDateArr = repaymentDateTmp.split("-");
        if (repaymentDateArr.length > 2) {
            repaymentDate = repaymentDateArr[2];
        }
        // 账单日（不包含年月）#cphBody_MonthBillDay > a > span
        billDate = doc.select("#cphBody_MonthBillDay > a > span").text().trim().replaceAll("日", "");
        if(StringUtils.isBlank(billDate)){
            logger.info("taskKey={},账单日获取失败，设置默认的账单日防止解析异常",context.get(LUA_TASK_KEY))
            billDate = "16";
        }
        // 可用信用额度
        creditCardAllData.setBalance(availableLimitBD);
        // 信用额度
        creditCardAllData.setCreditLimit(new BigDecimal(creditLimit));
        creditCardAllData.setCashBalance(null);
        // 可用现金额度
        creditCardAllData.setCashLimit(new BigDecimal(enchashmentImprest));
    } catch (e) {
        logger.error("taskKey={} 账户信息抓取异常：{}", context.get(LUA_TASK_KEY), e);
        statCaughtException(context, e);
    }
}

/**
 * 抓取已出
 */
function crawlBillsInfo(bills, creditCardAllData, cardNum) {
    logger.info("=========招商手机银行已出账单信息==========");
    try {
        var parametersMap = new HashMap();
        parametersMap.put("ClientNo", clientNo);
        parametersMap.put("Command", "");
        parametersMap.put("XmlReq", "");
        Thread.sleep(3000);
        //var html = post(urls.BILL_LIST_URL, parametersMap, accountAndcardNoAndbilloutAndbillHeaders());
        var html = cmbSubmitWithReTry(urls.BILL_LIST_URL,parametersMap,accountAndcardNoAndbilloutAndbillHeaders(),"已出账单信息","post");
        storeDataToFile(context, html, cardNum + "_bill_option.html");
        var doc = Jsoup.parse(html);
        var creditAccNo = doc.select("#cphBody_ucCreditCardNo_CreditAccNo_selectCtl > option:first-child").val();

        //12个月
        var sixMon = getRecentMonth();
        for (var i = 0; i < sixMon.size(); i++) {
            //获得每个月
            var oneMonth = sixMon.get(i);
            var yearStr = oneMonth.substring(0, 4);
            yearStr = Integer.parseInt(yearStr);
            var monStr = Integer.parseInt(oneMonth.substring(5, 7));
            logger.info("12个月中的每一个月:{},{}", yearStr, monStr);
            var installments = new ArrayList();
            var shopping_sheets = new ArrayList();
            var billAllData = new BillAllData();
            // 银行名字
            billAllData.setBankName("招商银行");
            // 账单月份 yyyy-MM
            billAllData.setBillMonth(oneMonth);
            // 账单日yyyy-MM-dd
            billAllData.setBillDate(yyyy_MM_dd_PARSER.parse(oneMonth + "-" + billDate));
            // 账单类型
            billAllData.setBillType("DONE");
            billAllData.setBillId(UUID.randomUUID().toString());
            billAllData.setCreditLimit(creditCardAllData.getCreditLimit());
            billAllData.setCashLimit(creditCardAllData.getCashLimit());
            billAllData.setUsdNewBalance(null);
            billAllData.setUsdMinPayment(null);
            billAllData.setUsdLastBalance(null);
            billAllData.setUsdLastPayment(null);
            billAllData.setUsdNewCharges(null);
            billAllData.setUsdInterest(null);
            billAllData.setInterest(null);
            billAllData.setUsdCreditLimit(null);
            billAllData.setUsdCashLimit(null);

            oneMonth = oneMonth.replaceAll("-", "");

            getDetailBillByDate(creditAccNo, oneMonth, monStr, yearStr, billAllData, shopping_sheets, bills,
                creditCardAllData, installments, cardNum, i);
            //账单月和还款月是否相等
            if ("" !== billMonth && "" !== payMonth && payMonth.matches("[0-9]*")) {
                if (equals(billMonth, payMonth)) {
                    billAllData.setPaymentDueDate(yyyy_MM_dd_PARSER.parse(yearStr + "-" + billMonth + "-" + repaymentDate));
                } else {
                    var monStrBill = "";
                    var monBill = monStr + 1;
                    monBill =  Integer.parseInt(monBill);
                    logger.info("{},已出账单的还款月:{}",context.get(LUA_TASK_KEY),monBill);
                    if (monBill < 10) {
                        monStrBill = "0" + monBill;
                    } else if (monStr == 12) {
                        monStrBill = "01";
                        yearStr = Integer.parseInt(yearStr) + 1;
                    } else {
                        monStrBill = "" + monBill;
                    }
                    yearStr = Integer.parseInt(yearStr);
                    logger.info("{},已出账单的年:{}",context.get(LUA_TASK_KEY),yearStr);
                    // 还款日yyyy-MM-dd
                    billAllData.setPaymentDueDate(yyyy_MM_dd_PARSER.parse(yearStr + "-" + monStrBill + "-" + repaymentDate));

                }
            }else {//
                var monStrBill = "";
                var monBill = monStr + 1;
                monBill =  Integer.parseInt(monBill);
                logger.info("{},没有已出账单记录的,已出账单的还款月:{}",context.get(LUA_TASK_KEY),monBill);
                if (monBill < 10) {
                    monStrBill = "0" + monBill;
                } else if (monStr == 12) {
                    monStrBill = "01";
                    yearStr = Integer.parseInt(yearStr) + 1;
                } else {
                    monStrBill = "" + monBill;
                }
                yearStr = Integer.parseInt(yearStr);
                logger.info("{},没有已出账单记录的,已出账单的年:{}",context.get(LUA_TASK_KEY),yearStr);
                if(equals("",repaymentDate)){
                    // 还款日yyyy-MM-dd
                    billAllData.setPaymentDueDate(yyyy_MM_dd_PARSER.parse(yearStr + "-" + monStrBill + "-05"));
                }else {
                    // 还款日yyyy-MM-dd
                    billAllData.setPaymentDueDate(yyyy_MM_dd_PARSER.parse(yearStr + "-" + monStrBill + "-" + repaymentDate));
                }
            }

            bills.add(billAllData);
        }
    } catch (e) {
        logger.error("taskKey={} 已出账单抓取异常：{}", context.get(LUA_TASK_KEY), e);
        statCaughtException(context, e);
    }
}

/**
 * 抓取未出
 */
function crawlBillOut(bills, creditCardAllData, cardNum, billAllData) {
    logger.info("{},招商手机银行抓取未出账单信息", context.get(LUA_TASK_KEY));
    try {
        var cardNo = "";
        billAllData.setBillId(UUID.randomUUID().toString());
        billAllData.setBankName("招商银行");
        billAllData.setBillType("UNDONE");
        billAllData.setCreditLimit(creditCardAllData.getCreditLimit());
        billAllData.setCashLimit(creditCardAllData.getCashLimit());
        billAllData.setUsdNewBalance(null);
        billAllData.setUsdMinPayment(null);
        billAllData.setUsdLastBalance(null);
        billAllData.setUsdLastPayment(null);
        billAllData.setUsdNewCharges(null);
        billAllData.setUsdInterest(null);
        billAllData.setUsdCreditLimit(null);
        billAllData.setUsdCashLimit(null);
        billAllData.setMinPayment(null);
        billAllData.setInterest(null);
        billAllData.setLastPayment(null);
        var lastBalance = contextMap.get("newBalance");
        logger.info("{},上期账单金额:{}", context.get(LUA_TASK_KEY), lastBalance);
        billAllData.setLastBalance(null !== lastBalance ? new BigDecimal(lastBalance) : null);
        var calendar = Calendar.getInstance();
        var year = calendar.get(Calendar.YEAR);
        year = Integer.parseInt(year);
        var month = calendar.get(Calendar.MONTH) + 1;// 获取当前月份
        month = Integer.parseInt(month);
        var datenum = calendar.get(Calendar.DAY_OF_MONTH);// 获取当前日期
        datenum = Integer.parseInt(datenum);
        var cuMonth = "";

        if (datenum > Integer.parseInt(billDate)) {
            if ((month + 1) < 10) {
                cuMonth = "0" + String.valueOf((month + 1));
            } else if (month == 12) {
                cuMonth = "01";
                year = year + 1;
            } else {
                cuMonth = String.valueOf((month + 1));
            }
        } else {
            if (month < 10) {
                cuMonth = "0" + String.valueOf(month);
            } else if (month == 12) {
                cuMonth = "01";
                year = year + 1;
            } else {
                cuMonth = String.valueOf(month);
            }
        }
        year = Integer.parseInt(year);
        cuMonth = cuMonth.replace(".0","")
        if(cuMonth.length == 1){
            cuMonth = "0" + cuMonth;
        }
        // 日期
        billAllData.setBillDate(yyyy_MM_dd_PARSER.parse(year + "-" + cuMonth + "-" + billDate));
        // 月
        billAllData.setBillMonth(year + "-" + cuMonth);
        logger.info("开始抓取未出账单 :{}-{}-{}", year, cuMonth, billDate);
        var shopping_sheets = new ArrayList();
        var installments = new ArrayList();
        var parametersMap = new HashMap();
        parametersMap.put("ClientNo", clientNo);
        parametersMap.put("Command", "");
        parametersMap.put("XmlReq", "");
        Thread.sleep(3000);
        //var html = post(urls.WEI_OUT_URL, parametersMap, accountAndcardNoAndbilloutAndbillHeaders());

        var html = cmbSubmitWithReTry(urls.WEI_OUT_URL,parametersMap,accountAndcardNoAndbilloutAndbillHeaders(),"未出账单第一页","post")
        storeDataToFile(context, html, cardNum + "_wei_out_1Page.html");// 第一页

        if (html.contains("未查询到未出账单记录！") || StringUtils.isBlank(html)) {
            return;
        }

        var cacheIdFirst = html.lastIndexOf("CacheID\", \"");
        var cacheIdEnd = html.indexOf("\");", cacheIdFirst);
        var cacheIdStart = cacheIdFirst + ("CacheID\", \"".length());

        var cacheID = html.substring(cacheIdStart, cacheIdEnd);

        var doc = Jsoup.parse(html);
        var option = doc.select("#cphBody_ucCreditAccNo_CreditAccNo_selectCtl > option:nth-child(1)").first();
        var creditAccNo = option.val();
        var o_tot_amnt = doc.select("#cphBody_DataCont > ul > li > a > span").text().trim();
        var newCharges = o_tot_amnt.replaceAll("￥", "").replaceAll(",", "");// 1,241.43



        // 获取当前页和总页
        var page = doc.select("#cphBody_NextBack_divPageNo > div > span.ui-money").text().trim();
        var pages = null;

        if (page !== "") {
            pages = page.split("/");

        }

        var buttons = doc.select("li[onclick^=javascript:GotoDetail]");
        var i = 0;
        var installment = null;
        var item = 1;
        var sumNewBalance = 0.0;
        var weiOutNewCharges = 0.0;
        for (var j = 0; j < buttons.size(); j++) {
            // 消费
            var expense = new Expense();
            var recID = buttons.get(j).attr("onclick");
            recID = RegexUtils.GetText(recID, "javascript:GotoDetail\\((.*)\\);");
            recID = "[" + recID + "]";
            var first = recID.indexOf("['");
            var as = recID.indexOf("']", first);
            var start1 = first + ("['".length());
            var recId = recID.substring(start1, as);

            parametersMap = new HashMap();
            parametersMap.put("ClientNo", clientNo);
            parametersMap.put("Command", "CMD_VIEWDETAIL");
            parametersMap.put("DeviceTAB", "TabDefault");
            parametersMap.put("CreditAccNo", creditAccNo);
            parametersMap.put("PageNo", "1");
            parametersMap.put("RecID", recId);
            parametersMap.put("CacheID", cacheID);
            parametersMap.put("Currency", "156");
            parametersMap.put("O_TOT_AMNT", o_tot_amnt);
            Thread.sleep(3000);
            var billOutDetailHeader = new HashMap();
            billOutDetailHeader.put("User-Agent", params.USER_AGENT);
            billOutDetailHeader.put("Accept", params.ACCEPT);
            billOutDetailHeader.put("Host", "html.m.cmbchina.com");
            billOutDetailHeader.put("Origin", "https://html.m.cmbchina.com");
            billOutDetailHeader.put("Accept-Encoding", "gzip, deflate, br");
            billOutDetailHeader.put("Connection", "keep-alive");
            billOutDetailHeader.put("Content-Type", "application/x-www-form-urlencoded");
            billOutDetailHeader.put("Upgrade-Insecure-Requests", "1");

            //var htmlDetail = post(urls.WEI_OUT_DETAIL_URL, parametersMap, billOutDetailHeader);
            var htmlDetail = cmbSubmitWithReTry(urls.WEI_OUT_DETAIL_URL,parametersMap,billOutDetailHeader,"未出账单第一页详细信息","post")

            storeDataToFile(context, htmlDetail, cardNum + "_weiout_shopping_sheets_1Page_" + (item++) + "Item.html");

            var errorMsg = doc.select("#TEContent").text();

            if(html.contains("由于设备所连IP地址发生变化或登录后长时间未操作，存在安全隐患。为保障您的账户安全，请重新登录")){
                logger.info("taskKey = {},该月停止抓取，错误信息：由于设备所连IP地址发生变化或登录后长时间未操作，存在安全隐患。为保障您的账户安全，请重新登录", context.get(LUA_TASK_KEY));
                return
            }
            if (errorMsg.contains("详细信息查询失败!")) {
                logger.info("taskKey = {} ,招商银行抓取：银行响应错误信息{} , 该月停止抓取", context.get(LUA_TASK_KEY), errorMsg);
                return;
            }

            var docDetail = Jsoup.parse(htmlDetail);
            // 交易日期 #cphBody_EFFECTIVE_DTE
            var tranDate = docDetail.select("#cphBody_EFFECTIVE_DTE").text().trim();

            // 交易摘要#cphBody_DESC_NAME
            var transRemark = docDetail.select("#cphBody_DESC_NAME").text().trim().replaceAll("\\s*", "");
            var recordDate = docDetail.select("#cphBody_POSTING_DTE").text().trim();

            // 记账日期#cphBody_POSTING_DTE 2017-05-23
            var eL = "[0-9]{4}-[0-9]{2}-[0-9]{2}";
            var p1 = Pattern.compile(eL);
            var m1 = p1.matcher(recordDate);
            if (m1.matches()) {
                expense.setPostDate(yyyy_MM_dd_PARSER.parse(recordDate));
            } else {// 如果记账日期没有 就按交易日期的月份算   //如果这个是尚未入账
                expense.setPostDate(yyyy_MM_dd_PARSER.parse(tranDate));
            }
            if (((transRemark.contains("分期") && !transRemark.contains("DIY")) || transRemark.contains("苹果在线商店")) && remark_regx.test(transRemark)) {
                tranDate = getTheTransDate(transRemark,recordDate);
            }
            // 卡号末四位#cphBody_CARD_NMBR4
            cardNo = docDetail.select("#cphBody_CARD_NMBR4").text().trim();
            // 交易金额 #cphBody_TXN_CURR_AMT
            var transAmount = docDetail.select("#cphBody_TXN_CURR_AMT").text().trim().replaceAll("￥", "").replaceAll(",", "");
            if (equals(transAmount, "0.00")) {
                transAmount = docDetail.select("#cphBody_AMNT").text().trim().replaceAll("￥", "").replaceAll(",", "");
            }
            if(!transAmount.contains("-")){
                weiOutNewCharges =  weiOutNewCharges + Double.parseDouble(transAmount);
            }
            if(m1.matches()){
                sumNewBalance = sumNewBalance + Double.parseDouble(transAmount);
            }
            var transAmountBD = new BigDecimal(transAmount !== "" ? transAmount : "0");

            // 消费
            expense.setId(UUID.randomUUID().toString());
            expense.setDescription(transRemark);
            expense.setCardNum(cardNo);
            expense.setBalance(null);
            expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(tranDate + " 00:00:00"));
            expense.setCurrencyType("CNY");
            expense.setAmountMoney(transAmountBD);
            // 未出账单的分类
            expense.setCategory(CategoryUtil.getCreditCategory("CMB", transRemark, transAmount));
            expense.setTransMethod("");//交易方式
            expense.setTransChannel("");//交易通道
            expense.setRemark("");//备注
            expense.setOppositeCardNo("");//对方卡号
            expense.setNameOnOppositeCard("");//对方持卡人
            expense.setOppositeBank("");//对方银行
            expense.setTransAddr("");

            expense.setOrderIndex(++i);
            shopping_sheets.add(expense);

            if ((transRemark.contains("分期") && !transRemark.contains("手续费") && !transRemark.contains("DIY")) || transRemark.contains("苹果在线商店")) {// 分期
                if (getFen(transRemark).size() > 1) {
                    installment = new Installment();
                    billAllData.getInstallments().add(installment);//添加对象   但对象里面的值会自动同步
                    // 分期
                    installment.setShoppingsheetId(UUID.randomUUID().toString());
                    // 记账时间
                    installment.setPostDate(yyyy_MM_dd_PARSER.parse(recordDate));
                    // 交易时间
                    installment.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(tranDate + " 00:00:00"));
                    // 分期描述
                    installment.setInstallmentDesc(transRemark);
                    // 分期类型installment_type
                    installment.setInstallmentType(CreditResultUtil.installmentTypeChange(transRemark));
                    installment.setTransferFee(null);
                    installment.setTransferfeeDesc("")
                    // 总分期数 total_month
                    installment.setTotalMonth(getFen(transRemark).get(1));
                    // 当前分期数
                    installment.setCurrentMonth(getFen(transRemark).get(0));
                    // 币种
                    installment.setCurrencyType("CNY");
                    // 本期金额
                    installment.setAmountMoney(transAmountBD);
                }

            } else if (installment !=null && isNumExists(transRemark) && transRemark.contains("分期") && transRemark.contains("手续费") && !transRemark.contains("DIY")) {// 手续费
                // 分期手续费描述
                installment.setHandingfeeDesc(transRemark !== null ? transRemark : "");
                // 本期手续费
                installment.setHandingFee(transAmountBD);
            }
            //billAllData.getInstallments()获取现有的分期数据，如果有分期手续费，对交易日期进行比对，如果符合就将手续费放入这条分期数据
            /*if(transRemark.contains("分期") && transRemark.contains("手续费") && getFen(transRemark).size() > 1){
                for(var t =0;t<billAllData.getInstallments().size();t++){
                    print()
                    if(yyyy_MM_dd_PARSER.parse(recordDate) == billAllData.getInstallments().get(t).getPostDate() && transRemark.replace(/分期手续费/g,"").equals(billAllData.getInstallments().get(t).getInstallmentDesc())){
                        //说明这条记录相符，将这条记录中的金额放入手续费，描述也放入
                        billAllData.getInstallments().get(t).setHandingfeeDesc(transRemark !== null ? transRemark : "")
                        billAllData.getInstallments().get(t).setHandingFee(transAmountBD)
                    }
                }
            }*/


        }

        if (page !== "") {
            for (var currentPage = 2; currentPage <= Integer.parseInt(pages[1]); currentPage++) {
                parametersMap.clear();
                parametersMap.put("CreditAccNo", creditAccNo);
                parametersMap.put("Currency", "156");
                parametersMap.put("PageNo", currentPage + "");
                parametersMap.put("Command", "");
                parametersMap.put("ClientNo", clientNo);
                parametersMap.put("DeviceTAB", "TabDefault");
                Thread.sleep(3000);
                var billOutNextPageHeader = new HashMap();
                billOutNextPageHeader.put("User-Agent", params.USER_AGENT);
                billOutNextPageHeader.put("Accept", params.ACCEPT);
                billOutNextPageHeader.put("Host", "html.m.cmbchina.com");
                billOutNextPageHeader.put("Origin", "https://html.m.cmbchina.com");
                billOutNextPageHeader.put("Accept-Encoding", "gzip, deflate, br");
                billOutNextPageHeader.put("Connection", "keep-alive");
                billOutNextPageHeader.put("Content-Type", "application/x-www-form-urlencoded");
                billOutNextPageHeader.put("Upgrade-Insecure-Requests", "1");
                //var pageNextXml = post(urls.WEI_OUT_DETAIL_URL, parametersMap, billOutNextPageHeader);
                var pageNextXml = cmbSubmitWithReTry(urls.WEI_OUT_DETAIL_URL,parametersMap,billOutNextPageHeader,"未出账单第"+currentPage+"页","post")
                storeDataToFile(context, pageNextXml, cardNum + "_wei_out_" + currentPage + "Page.html");
                var pageNextDoc = Jsoup.parse(pageNextXml);
                var buttonsPage = pageNextDoc.select("li[onclick^=javascript:GotoDetail]");
                var item1 = (10 * (currentPage - 1));
                // for (Element button : buttonsPage) {
                for (var it = 0; it < buttonsPage.size(); it++) {
                    // 消费
                    var expense = new Expense();

                    var recID = buttonsPage.get(it).attr("onclick");
                    logger.info("recID = {} ", recID);
                    recID = RegexUtils.GetText(recID, "javascript:GotoDetail\\((.*)\\);");
                    recID = "[" + recID + "]";
                    var first = recID.indexOf("['");
                    var as = recID.indexOf("']", first);
                    var start1 = first + ("['".length());
                    var news = recID.substring(start1, as);

                    parametersMap.clear();
                    parametersMap.put("ClientNo", clientNo);
                    parametersMap.put("Command", "CMD_VIEWDETAIL");
                    parametersMap.put("DeviceTAB", "TabDefault");
                    parametersMap.put("CreditAccNo", creditAccNo);
                    parametersMap.put("PageNo", currentPage + "");
                    parametersMap.put("RecID", news);
                    parametersMap.put("CacheID", cacheID);
                    parametersMap.put("Currency", "156");
                    parametersMap.put("O_TOT_AMNT", o_tot_amnt);
                    Thread.sleep(3000);
                    var pageHeader = new HashMap();
                    pageHeader.put("Accept", params.ACCEPT);
                    pageHeader.put("Host", "html.m.cmbchina.com");
                    pageHeader.put("Origin", "https://html.m.cmbchina.com");
                    pageHeader.put("Referer", "https://html.m.cmbchina.com/MobileHtml/CreditCard/M_AccountManage/am_QueryReckoningSurveyNot.aspx");
                    pageHeader.put("User-Agent", params.USER_AGENT);

                    //var htmlDetail = post(urls.WEI_OUT_DETAIL_URL, parametersMap, pageHeader);
                    var htmlDetail = cmbSubmitWithReTry(urls.WEI_OUT_DETAIL_URL,parametersMap,pageHeader,"详细信息查询","post")
                    storeDataToFile(context, htmlDetail,
                        cardNum + "_weiout_bill_shopping_sheets_" + currentPage + "Page_" + (++item1) + "Item.html");
                    var errorMsg = doc.select("#TEContent").text();
                    if (errorMsg.contains("详细信息查询失败!")) {
                        logger.info("任务{},招商银行抓取：银行响应错误信息{}，该月停止抓取", context.get(LUA_TASK_KEY), errorMsg);
                        return;
                    }

                    var docDetail = Jsoup.parse(htmlDetail);
                    // 交易日期 #cphBody_EFFECTIVE_DTE
                    var tranDate = docDetail.select("#cphBody_EFFECTIVE_DTE").text().trim();
                    // 交易摘要#cphBody_DESC_NAME
                    var transRemark = docDetail.select("#cphBody_DESC_NAME").text().trim().replaceAll("\\s*", "");
                    // 记账日期#cphBody_POSTING_DTE 2017-05-23
                    var recordDate = docDetail.select("#cphBody_POSTING_DTE").text().trim();
                    var eL = "[0-9]{4}-[0-9]{2}-[0-9]{2}";
                    var p1 = Pattern.compile(eL);
                    var m1 = p1.matcher(recordDate);
                    if(!equals(recordDate,"") && !equals(tranDate,"")){
                        if (m1.matches()) {
                            expense.setPostDate(yyyy_MM_dd_PARSER.parse(recordDate));
                        } else {// 如果记账日期没有 就按交易日期的月份算
                            expense.setPostDate(yyyy_MM_dd_PARSER.parse(tranDate));
                        }
                    }
                    if (((transRemark.contains("分期") && !transRemark.contains("DIY")) || transRemark.contains("苹果在线商店")) && remark_regx.test(transRemark)) {
                        tranDate = getTheTransDate(transRemark,recordDate);
                    }

                    // 交易金额 #cphBody_TXN_CURR_AMT
                    var transAmount = docDetail.select("#cphBody_TXN_CURR_AMT").text().trim().replaceAll("￥", "").replaceAll(",", "");
                    if(m1.matches()){
                        sumNewBalance = sumNewBalance + Double.parseDouble(transAmount);
                    }
                    if(!transAmount.contains("-") && !equals(transAmount,"")){
                        weiOutNewCharges =  weiOutNewCharges + Double.parseDouble(transAmount);
                    }
                    var transAmountBD = null;
                    if (transAmount !== "") {
                        transAmountBD = new BigDecimal(transAmount);
                    }
                    // 消费
                    expense.setId(UUID.randomUUID().toString());
                    expense.setDescription(transRemark);
                    expense.setCardNum(cardNo);
                    if(!equals(tranDate,"")){
                        expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(tranDate + " 00:00:00"));
                    }
                    expense.setCurrencyType("CNY");
                    expense.setAmountMoney(transAmountBD);
                    expense.setBalance(null);
                    // 未出账单的分类
                    expense.setCategory(CategoryUtil.getCreditCategory("CMB", transRemark, transAmount));
                    expense.setTransMethod("");//交易方式
                    expense.setTransChannel("");//交易通道
                    expense.setRemark("");//备注
                    expense.setOppositeCardNo("");//对方卡号
                    expense.setNameOnOppositeCard("");//对方持卡人
                    expense.setOppositeBank("");//对方银行
                    expense.setTransAddr("");
                    expense.setOrderIndex(++i);
                    shopping_sheets.add(expense);

                    if ((transRemark.contains("分期")
                            && !transRemark.contains("手续费")
                            && !transRemark.contains("DIY")) || transRemark.contains("苹果在线商店")) {// 分期
                        if (getFen(transRemark).size() > 1) {
                            installment = new Installment();
                            billAllData.getInstallments().add(installment);//添加对象   但对象里面的值会自动同步
                            // 分期
                            installment.setShoppingsheetId(UUID.randomUUID().toString());
                            // 记账时间
                            installment.setPostDate(yyyy_MM_dd_PARSER.parse(recordDate));
                            // 交易时间
                            installment.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(tranDate + " 00:00:00"));
                            // 分期描述
                            installment.setInstallmentDesc(transRemark);
                            // 分期类型installment_type
                            installment.setInstallmentType(CreditResultUtil.installmentTypeChange(transRemark));
                            installment.setTransferFee(null);
                            installment.setTransferfeeDesc("")
                            // 总分期数 total_month
                            installment.setTotalMonth(getFen(transRemark).get(1));
                            // 当前分期数
                            installment.setCurrentMonth(getFen(transRemark).get(0));
                            // 币种
                            installment.setCurrencyType("CNY");
                            // 本期金额
                            installment.setAmountMoney(transAmountBD);
                        }
                    }else if (installment !=null && isNumExists(transRemark) && transRemark.contains("分期") && transRemark.contains("手续费") && !transRemark.contains("DIY")) {// 手续费
                        // 分期手续费描述
                        installment.setHandingfeeDesc(transRemark);
                        // 本期手续费
                        installment.setHandingFee(transAmountBD);
                    }

                    /*if(transRemark.contains("分期") && transRemark.contains("手续费") && getFen(transRemark).size() > 1){
                        for(var t =0;t<billAllData.getInstallments().size();t++){
                            if(yyyy_MM_dd_PARSER.parse(recordDate) == billAllData.getInstallments().get(t).getPostDate() && transRemark.replace(/分期手续费/g,"").equals(billAllData.getInstallments().get(t).getInstallmentDesc())){
                                //说明这条记录相符，将这条记录中的金额放入手续费，描述也放入
                                billAllData.getInstallments().get(t).setHandingfeeDesc(transRemark !== null ? transRemark : "")
                                billAllData.getInstallments().get(t).setHandingFee(transAmountBD)
                            }
                        }
                    }*/

                }
            }
        }
        if(lastBalance !== null){
            sumNewBalance = sumNewBalance + Double.parseDouble(lastBalance);
        }
        billAllData.setNewCharges(new BigDecimal(String.format("%.2f", weiOutNewCharges)));
        billAllData.setNewBalance(new BigDecimal(String.format("%.2f", sumNewBalance)));
        if (installments.size() != 0) {
            billAllData.setInstallments(installments);
        }
        billAllData.setShoppingSheets(shopping_sheets);
    } catch (e) {
        logger.error("taskKey={},未出账单抓取异常{}", context.get(LUA_TASK_KEY), e);
        statCaughtException(context, e);
    }


}


/**
 * 已出账单明细
 */
function getDetailBillByDate(creditAccNo, oneMonth, month, year, billAllData, shopping_sheets, bills, creditCardAllData,
                             installments, cardNum, q) {
    logger.info("招商银行抓取年月: {} ", oneMonth);
    try {
        var mon = "";
        if (String.valueOf(month).length() == 1) {
            mon = "0" + String.valueOf(month);
        } else {
            mon = String.valueOf(month);
        }
        var parameters = new HashMap();
        parameters = new HashMap();
        parameters.put("ClientNo", clientNo);
        parameters.put("Currency", "156");
        parameters.put("CreditAccNo", creditAccNo);
        parameters.put("ColumnOfDate", oneMonth);
        parameters.put("Command", "CMD_DETAIL");
        parameters.put("DeviceTAB", "TabDefault");

        // 对账单详细信息页请求地址
        var billDetailHeader = new HashMap();
        billDetailHeader.put("User-Agent", params.USER_AGENT);
        billDetailHeader.put("Accept", params.ACCEPT);
        billDetailHeader.put("Host", "html.m.cmbchina.com");
        billDetailHeader.put("Origin", "https://html.m.cmbchina.com");
        billDetailHeader.put("Referer", "https://html.m.cmbchina.com/MobileHtml/CreditCard/M_AccountManage/am_QueryReckoningList.aspx");
        billDetailHeader.put("Accept-Encoding", "gzip, deflate, br");
        billDetailHeader.put("Connection", "keep-alive");
        billDetailHeader.put("Content-Type", "application/x-www-form-urlencoded");
        billDetailHeader.put("Upgrade-Insecure-Requests", "1");

        //var html = post(urls.BILL_URL, parameters, billDetailHeader);
        var html = cmbSubmitWithReTry(urls.BILL_URL,parameters,billDetailHeader,"已出账单明细月份"+oneMonth,"post");

        storeDataToFile(context, html, cardNum + "_bill_" + mon + "mon_benqiPage.html");
        var doc = Jsoup.parse(html);
        var errorMsg = doc.select("#TEContent").text();
        if(html.contains("由于设备所连IP地址发生变化或登录后长时间未操作，存在安全隐患。为保障您的账户安全，请重新登录")){
            logger.info("taskKey = {},该月停止抓取，错误信息：由于设备所连IP地址发生变化或登录后长时间未操作，存在安全隐患。为保障您的账户安全，请重新登录", context.get(LUA_TASK_KEY));
            return
        }
        if (StringUtils.isNotBlank(errorMsg)) {
            logger.info("taskKey = {},该月停止抓取，错误信息：{}", context.get(LUA_TASK_KEY), errorMsg);
            return;
        }

        // 本期还款金额 #cphBody_liBQYHJE > a > span #cphBody_AccInfoDT >
        var sumBalance = doc.select("#cphBody_AccInfoDT > ul:nth-child(2) > li:nth-child(1) > a > span").text()
            .trim().replaceAll("￥", "").replaceAll(",", "");
        q = Integer.parseInt(q);
        if (q == 0) {
            logger.info("q={}", q);
            contextMap.put("newBalance", sumBalance);
        }
        // 最低还款金额 #cphBody_AccInfoDT > ul:nth-child(2) > li:nth-child(2) >
        var minRepayment = doc
            .select("#cphBody_AccInfoDT > ul:nth-child(2) > li:nth-child(2) > a:nth-child(1) > span:nth-child(1)")
            .text().trim().replaceAll("￥", "").replaceAll(",", "");

        // 上期账单金额
        var lastBalance = doc
            .select("#cphBody_AccInfoDT > ul:nth-child(2) > li:nth-child(3) > a:nth-child(1) > span:nth-child(1)")
            .text().trim().replaceAll("￥", "").replaceAll(",", "");

        // 上期还款金额
        var lastRepayment = doc
            .select("#cphBody_AccInfoDT > ul:nth-child(2) > li:nth-child(4) > a:nth-child(1) > span:nth-child(1)")
            .text().trim().replaceAll("￥", "").replaceAll(",", "");

        // 本期账单金额
        var newBalance = doc.select("#cphBody_AccInfoDT > ul:nth-child(2) > li:nth-child(5) > a > span").text()
            .trim().replaceAll("￥", "").replaceAll(",", "");

        // 循环利息
        var interest = doc
            .select("#cphBody_AccInfoDT > ul:nth-child(2) > li:nth-child(7) > a:nth-child(1) > span:nth-child(1)")
            .text().trim().replaceAll("￥", "").replaceAll(",", "");
        // 最低还款金额
        billAllData.setMinPayment(minRepayment !== "" ? new BigDecimal(minRepayment) : new BigDecimal(0));
        // 本期应还金额
        billAllData.setNewBalance(sumBalance !== "" ? new BigDecimal(sumBalance) : new BigDecimal(0));
        // 上期账单金额
        billAllData.setLastBalance(lastBalance !== "" ? new BigDecimal(lastBalance) : new BigDecimal(0));
        // 上期还款金额
        billAllData.setLastPayment(lastRepayment !== "" ? new BigDecimal("-" + lastRepayment) : new BigDecimal(0));
        // 本期账单金额
        billAllData.setNewCharges(newBalance !== "" ? new BigDecimal(newBalance) : new BigDecimal(0));
        // 循环利息
        billAllData.setInterest(interest !== "" ? new BigDecimal(interest) : new BigDecimal(0));

        parameters = new HashMap();
        parameters.put("ClientNo", clientNo);
        parameters.put("Currency", "156");
        parameters.put("CreditAccNo", creditAccNo);
        parameters.put("ColumnOfDate", oneMonth);
        parameters.put("DeviceTAB", "TabDefault");

        // 对账单详细信息页请求地址
        var billHeader = new HashMap();
        billHeader.put("User-Agent", params.USER_AGENT);
        billHeader.put("Accept", params.ACCEPT);
        billHeader.put("Host", "html.m.cmbchina.com");
        billHeader.put("Origin", "https://html.m.cmbchina.com");
        billHeader.put("Referer", "https://html.m.cmbchina.com/MobileHtml/creditcard/m_accountmanage/am_queryreckoningsurvey.aspx");
        billHeader.put("Accept-Encoding", "gzip, deflate, br");
        billHeader.put("Connection", "keep-alive");
        billHeader.put("Content-Type", "application/x-www-form-urlencoded");
        billHeader.put("Upgrade-Insecure-Requests", "1");

        html = cmbSubmitWithReTry(urls.BILL_URL,parameters,billHeader,"对账单详细页"+mon,"post");
        //html = post(urls.BILL_URL, parameters, billHeader);
        storeDataToFile(context, html, cardNum + "_bill_shopping_sheets_" + mon + "mon_1Page.html");
        doc = Jsoup.parse(html);

        billMonth = doc.select("#cphBody_DataContent1 > ul:nth-child(1) > li:nth-child(1) > a:nth-child(1) > span:nth-child(1)").text().trim().replaceAll("-", "").substring(4, 6);

        payMonth = doc.select("#cphBody_DataContent1 > ul:nth-child(1) > li:nth-child(2) > a:nth-child(1) > span:nth-child(1)").text().trim();
        if (payMonth.matches("[0-9]{4}-[0-9]{2}-[0-9]{2}")) {
            payMonth = payMonth.replaceAll("-", "").substring(4, 6);
        }
        logger.info("{},账单月{},还款月{}", context.get(LUA_TASK_KEY),billMonth, payMonth);
        errorMsg = doc.select("#TEContent").text();

        if (StringUtils.isNotBlank(errorMsg)) {
            logger.info("{ } 招商银行抓取：银行响应错误信息{}，该月停止抓取", context.get(LUA_TASK_KEY), errorMsg);
            return;
        }

        var i = 0;
        var buttons = doc.select("li[onclick^=javascript:QueryListDetail]");
        // 获取总页数
        var pageSum = doc.select("#cphBody_NextBack_divPageNo > div > span.ui-money").text().trim();
        var pages = null;
        if (pageSum !== "") {
            pages = pageSum.split("/");
        }

        var installment = null;

        var lastInstallList = null;
        for (var item = 0; item < buttons.size(); item++) {
            var expenseList = getList(buttons, item);
            if(expenseList.size()<8){
                continue;
            }
            // 消费
            var expense = new Expense();
            // 交易金额
            var transAmount = expenseList.get(3).trim().replaceAll(",", "").replaceAll("￥", "");
            var amount_money = null;

            // 交易摘要
            var description = expenseList.get(2).trim().replaceAll("\\s*", "");
            expense.setDescription(description);
            expense.setId(UUID.randomUUID().toString());

            var record_date_exp = "";
            if (StringUtils.isNotBlank(expenseList.get(1).trim())) {
                if (1 == month && equals("12", expenseList.get(1).trim().split("-")[0])) {
                    // 记账日期
                    record_date_exp = (Integer.parseInt(year) - 1) + "-" + expenseList.get(1).trim()
                    expense.setPostDate(yyyy_MM_dd_PARSER.parse(record_date_exp));
                } else {
                    // 记账日期
                    record_date_exp = year + "-" + expenseList.get(1).trim();
                    expense.setPostDate(yyyy_MM_dd_PARSER.parse(record_date_exp));
                }
            }

            if(((description.contains("分期") && !description.contains("DIY")) || description.contains("苹果在线商店") && !description.contains("￥")) && remark_regx.test(description)){
                var trans_date = getTheTransDate(description,record_date_exp);
                expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(trans_date + " 00:00:00"));
            }else{
                if (StringUtils.isNotBlank(expenseList.get(6).trim())) {
                    if (1 == month && equals("12", expenseList.get(6).trim().split("-")[0])) {
                        // 交易日期
                        expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year) - 1) + "-" + expenseList.get(6).trim() + " 00:00:00"));
                    } else {
                        // 交易日期
                        expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(year + "-" + expenseList.get(6).trim() + " 00:00:00"));
                    }
                } else {
                    if (1 == month && equals("12", expenseList.get(1).trim().split("-")[0])) {
                        // 交易日期
                        expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year) - 1) + "-" + expenseList.get(1).trim() + " 00:00:00"));
                    } else {
                        // 交易日期
                        expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(year + "-" + expenseList.get(1).trim() + " 00:00:00"));
                    }
                }
            }


            // 交易地点
            expense.setTransAddr(expenseList.get(5).trim());
            if (transAmount !== "") {
                amount_money = new BigDecimal(transAmount);
                // 交易金额
                expense.setAmountMoney(amount_money);
            } else {
                //交易金额
                expense.setAmountMoney(new BigDecimal(0));
            }
            // 交易币种
            expense.setCurrencyType("CNY");
            expense.setBalance(null);
            // 卡号末四位
            expense.setCardNum(expenseList.get(4).trim());
            // 已出账单的分类
            expense.setCategory(CategoryUtil.getCreditCategory("CMB", description, transAmount));
            expense.setTransMethod("");//交易方式
            expense.setTransChannel("");//交易通道
            expense.setRemark("");//备注
            expense.setOppositeCardNo("");//对方卡号
            expense.setNameOnOppositeCard("");//对方持卡人
            expense.setOppositeBank("");//对方银行

            expense.setOrderIndex(++i);
            shopping_sheets.add(expense);
            billAllData.setShoppingSheets(shopping_sheets);

            // 判断摘要是分期
            if (((description.contains("分期") && !description.contains("DIY")) || description.contains("苹果在线商店")) && !description.contains("￥") && !description.contains("手续费")) {// 分期
                //判断如果最后一条是分期数据。就把这个expenseList放到下一页取处理
                if(item == buttons.size()-1){
                    lastInstallList = expenseList;
                }else if (getFen(description).size() > 1) {
                    installment = new Installment();
                    billAllData.getInstallments().add(installment);//添加对象   但对象里面的值会自动同步
                    // 分期
                    installment.setShoppingsheetId(UUID.randomUUID().toString());
                    // 记账时间 post_date
                    var record_date = "";
                    if (StringUtils.isNotBlank(expenseList.get(1).trim())) {
                        if (1 == month && equals("12", expenseList.get(1).trim().split("-")[0])) {
                            // 记账日期
                            record_date = (Integer.parseInt(year) - 1) + "-" + expenseList.get(1).trim();
                            installment.setPostDate(yyyy_MM_dd_PARSER.parse(record_date));
                        } else {
                            // 记账日期
                            record_date = (Integer.parseInt(year)) + "-" + expenseList.get(1).trim();
                            installment.setPostDate(yyyy_MM_dd_PARSER.parse(record_date));
                        }

                    }
                    // 交易时间 trans_date
                    if (StringUtils.isNotBlank(expenseList.get(0).trim())) {
                        if(remark_regx.test(description)){
                            var trans_date = getTheTransDate(description,record_date);
                            installment.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(trans_date + " 00:00:00"));
                        }else{
                            if (1 == month && equals("12", expenseList.get(0).trim().split("-")[0])) {
                                // 交易日期
                                installment.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year) - 1) + "-" + expenseList.get(0).trim() + " 00:00:00"));
                            } else {
                                // 交易日期
                                installment.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year)) + "-" + expenseList.get(0).trim() + " 00:00:00"));
                            }
                        }
                    }
                    // 分期描述
                    installment.setInstallmentDesc(description);
                    // 分期类型installment_type
                    installment.setInstallmentType(CreditResultUtil.installmentTypeChange(description));
                    // 总分期数 total_month
                    installment.setTotalMonth(getFen(description).get(1));
                    // 当前分期数
                    installment.setCurrentMonth(getFen(description).get(0));
                    // 币种
                    installment.setCurrencyType("CNY");
                    installment.setTransferFee(null);
                    installment.setTransferfeeDesc("")
                    // 本期金额
                    installment.setAmountMoney(amount_money);
                    //
                    if((item+ 1) != buttons.size()){
                        // 分期
                        if ((item + 1) <= buttons.size()) {// 下一条是分期手续费
                            var installmentList = getList(buttons, (item + 1));
                            if (installmentList.size() == 8) {
                                // 分期描述
                                var handingFeeDesc = installmentList.get(2).trim().replaceAll("\\s*", "");
                                logger.info("{},这是下一条,手续费描述:{}", context.get(LUA_TASK_KEY), handingFeeDesc);
                                var handingFee = installmentList.get(3).trim().replaceAll(",", "").replaceAll("￥", "");
                                if(handingFeeDesc.matches(".*[0-9]{2,}-[0-9]{2,}")){
                                    logger.info("1019 -- {},分期里面的数字:handingFeeDesc[0]=:{}/handingFeeDesc[1]=:{}/description[0]=:{}/description[1]=:{}",context.get(LUA_TASK_KEY),getFen(handingFeeDesc).get(0),getFen(handingFeeDesc).get(1),getFen(description).get(0),getFen(description).get(1));
                                    if ( getFen(handingFeeDesc).get(0) == getFen(description).get(0)) {
                                        installment.setHandingFee(!equals("",handingFee) ? new BigDecimal(handingFee) : new BigDecimal(0));
                                        installment.setHandingfeeDesc(handingFeeDesc);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }


        // 判断下一页
        if (pageSum !== "") {
            for (var currentPage = 2; currentPage <= Integer.parseInt(pages[1]); currentPage++) {
                var url = "https://html.m.cmbchina.com/MobileHtml/CreditCard/M_AccountManage/am_QueryReckoningList.aspx";
                var map = new HashMap();
                map.clear();
                map.put("CreditAccNo", creditAccNo);
                map.put("Currency", "156");
                map.put("ColumnOfDate", oneMonth);
                map.put("PageNo", currentPage + "");
                map.put("Command", "");
                map.put("ClientNo", clientNo);
                map.put("DeviceTAB", "TabDefault");
                Thread.sleep(3000);
                var billHeaders = new HashMap();
                billHeaders.put("Referer", "https://html.m.cmbchina.com/MobileHtml/CreditCard/M_AccountManage/am_QueryReckoningList.aspx");
                billHeaders.put("Host", "html.m.cmbchina.com");
                billHeaders.put("Origin", "https://html.m.cmbchina.com");
                billHeaders.put("Accept", params.ACCEPT);
                billHeaders.put("User-Agent", params.USER_AGENT);

                //var currentPageXMl = post(url, map, billHeaders);
                var currentPageXMl = cmbSubmitWithReTry(url,map,billHeaders,"对账单详细页月份"+mon+"页数"+currentPage,"post");
                storeDataToFile(context, currentPageXMl,
                    cardNum + "_bill_shopping_sheets_" + mon + "mon_" + currentPage + "Page.html");
                var currentPageDoc = Jsoup.parse(currentPageXMl);
                var buttonCurrentPage = currentPageDoc.select("li[onclick^=javascript:QueryListDetail]");

                var install = null;
                for (var item = 0; item < buttonCurrentPage.size(); item++) {
                    var currentPagelist = null;
                    if(lastInstallList!=null && lastInstallList.size()>=1){
                        //说明上一条是分期数据，处理这条数据之后再处理处理之后的
                        item--;
                        currentPagelist = lastInstallList;
                        lastInstallList = null;
                    }else{
                        currentPagelist = getList(buttonCurrentPage, item);
                    }

                    if(currentPagelist.size()<8){
                        continue;
                    }
                    // 消费
                    var expense = new Expense();

                    // 交易金额
                    var transAmount = currentPagelist.get(3).trim().replaceAll(",", "").replaceAll("￥", "");
                    var amount_money = null;

                    // 交易摘要
                    var description = currentPagelist.get(2).trim().replaceAll("\\s*", "");
                    expense.setDescription(description);
                    expense.setId(UUID.randomUUID().toString());
                    var record_date_exp = "";
                    if (StringUtils.isNotBlank(currentPagelist.get(1).trim())) {
                        if (1 == month && equals("12", currentPagelist.get(1).trim().split("-")[0])) {
                            // 记账日期
                            record_date_exp = (Integer.parseInt(year) - 1) + "-" + currentPagelist.get(1).trim()
                            expense.setPostDate(yyyy_MM_dd_PARSER.parse(record_date_exp));
                        } else {
                            // 记账日期
                            record_date_exp = year + "-" + currentPagelist.get(1).trim()
                            expense.setPostDate(yyyy_MM_dd_PARSER.parse(record_date_exp));
                        }
                    }
                    if(((description.contains("分期") && !description.contains("DIY")) || description.contains("苹果在线商店") && !description.contains("￥")) && remark_regx.test(description)){
                        var trans_date = getTheTransDate(description,record_date_exp);
                        expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(trans_date + " 00:00:00"));
                    }else{
                        if (StringUtils.isNotBlank(currentPagelist.get(6).trim())) {
                            if (1 == month && equals("12", currentPagelist.get(6).trim().split("-")[0])) {
                                // 交易日期
                                expense.setTransDate(
                                    yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year) - 1) + "-" + currentPagelist.get(6).trim() + " 00:00:00"));
                            } else {
                                // 交易日期
                                expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(year + "-" + currentPagelist.get(6).trim() + " 00:00:00"));
                            }
                        } else {
                            if (1 == month && equals("12", currentPagelist.get(1).trim().split("-")[0])) {
                                // 交易日期
                                expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year) - 1) + "-" + currentPagelist.get(1).trim() + " 00:00:00"));
                            } else {
                                // 交易日期
                                expense.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(year + "-" + currentPagelist.get(1).trim() + " 00:00:00"));
                            }
                        }
                    }


                    // 交易地点
                    expense.setTransAddr(currentPagelist.get(5).trim());
                    if (transAmount !== "") {
                        amount_money = new BigDecimal(transAmount);
                        // 交易金额
                        expense.setAmountMoney(new BigDecimal(transAmount));
                    } else {
                        expense.setAmountMoney(new BigDecimal(0));
                    }
                    // 交易币种
                    expense.setCurrencyType("CNY");
                    expense.setBalance(null);
                    // 卡号末四位
                    expense.setCardNum(currentPagelist.get(4).trim());
                    // 已出账单的分类
                    expense.setCategory(CategoryUtil.getCreditCategory("CMB", description, transAmount));
                    expense.setOrderIndex(++i);

                    expense.setTransMethod("");//交易方式
                    expense.setTransChannel("");//交易通道
                    expense.setRemark("");//备注
                    expense.setOppositeCardNo("");//对方卡号
                    expense.setNameOnOppositeCard("");//对方持卡人
                    expense.setOppositeBank("");//对方银行

                    shopping_sheets.add(expense);
                    billAllData.setShoppingSheets(shopping_sheets);

                    // 判断摘要是分期
                    if (((description.contains("分期") && !description.contains("DIY")) || description.contains("苹果在线商店")) && !description.contains("￥") && !description.contains("手续费")) {// 分期
                        if(item == buttonCurrentPage.size()-1){
                            //分页中的最后一条如果是分期数据，同样放到下一页处理
                            lastInstallList = currentPagelist;
                        }else if (getFen(description).size() > 1) {
                            install = new Installment();
                            billAllData.getInstallments().add(install);//添加对象   但对象里面的值会自动同步
                            // 分期
                            install.setShoppingsheetId(UUID.randomUUID().toString());
                            // 记账时间 post_date
                            var record_date = "";
                            if (StringUtils.isNotBlank(currentPagelist.get(1).trim())) {
                                if (1 == month && equals("12", currentPagelist.get(1).trim().split("-")[0])) {
                                    // 记账日期
                                    record_date = (Integer.parseInt(year) - 1) + "-" + currentPagelist.get(1).trim()
                                    install.setPostDate(yyyy_MM_dd_PARSER.parse(record_date));
                                } else {
                                    // 记账日期
                                    record_date = (Integer.parseInt(year)) + "-" + currentPagelist.get(1).trim();
                                    install.setPostDate(yyyy_MM_dd_PARSER.parse(record_date));
                                }
                            }
                            // 交易时间 trans_date
                            if (StringUtils.isNotBlank(currentPagelist.get(0).trim())) {
                                if(remark_regx.test(description)){
                                    var trans_date = getTheTransDate(description,record_date);
                                    install.setTransDate(yyyy_MM_dd_HH_mm_ss_PARSER.parse(trans_date + " 00:00:00"));
                                }else{
                                    if (1 == month && equals("12", currentPagelist.get(0).trim().split("-")[0])) {
                                        // 交易日期
                                        install.setTransDate(
                                            yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year) - 1) + "-" + currentPagelist.get(0).trim() + " 00:00:00"));
                                    } else {
                                        // 交易日期
                                        install.setTransDate(
                                            yyyy_MM_dd_HH_mm_ss_PARSER.parse((Integer.parseInt(year)) + "-" + currentPagelist.get(0).trim() + " 00:00:00"));
                                    }
                                }
                            }
                            // 分期描述
                            install.setInstallmentDesc(description);
                            // 分期类型installment_type
                            install.setInstallmentType(CreditResultUtil.installmentTypeChange(description));
                            install.setTransferFee(null);
                            install.setTransferfeeDesc("")
                            // 总分期数 total_month
                            install.setTotalMonth(getFen(description).get(1));
                            // 当前分期数
                            install.setCurrentMonth(getFen(description).get(0));
                            // 币种
                            install.setCurrencyType("CNY");
                            // 本期金额
                            install.setAmountMoney(amount_money);

                            if((item + 1) != buttonCurrentPage.size()){
                                // 分期
                                if ((item + 1) <= buttonCurrentPage.size()) {// 下一条是分期手续费
                                    var installmentList = getList(buttonCurrentPage, (item + 1));
                                    if (installmentList.size() == 8) {
                                        // 分期手续费
                                        var handingFeeDesc = installmentList.get(2).trim().replaceAll("\\s*", "");
                                        logger.info("{}页,{},这是下一条,手续费描述:{}", currentPage, context.get(LUA_TASK_KEY), handingFeeDesc);
                                        var handingFee = installmentList.get(3).trim().replaceAll(",", "").replaceAll("￥", "");
                                        if(handingFeeDesc.matches(".*[0-9]{2,}-[0-9]{2,}")){
                                            logger.info("{}页,-- {},分期里面的数字:handingFeeDesc[0]=:{}/handingFeeDesc[1]=:{}/description[0]=:{}/description[1]=:{}",currentPage,context.get(LUA_TASK_KEY),getFen(handingFeeDesc).get(0),getFen(handingFeeDesc).get(1),getFen(description).get(0),getFen(description).get(1));
                                            if (getFen(handingFeeDesc).get(0) == getFen(description).get(0)) {
                                                install.setHandingFee(!equals("",handingFee) ? new BigDecimal(handingFee) : new BigDecimal(0));
                                                install.setHandingfeeDesc(handingFeeDesc);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        if (installments.size() != 0) {
            billAllData.setInstallments(installments);
        }

    } catch (e) {
        logger.error("taskKey={} 已出账单抓取异常：{},招商银行抓取年月: {}", context.get(LUA_TASK_KEY), e, oneMonth);
        statCaughtException(context, e);
    }

}

/**
 * 判断字符串里面是否包含数字
 * @param description
 * @return
 */
function isNumExists(description) {
    return description.matches(".*\\d+.*");
}

/**
 * 获取事件 描述的
 * @param buttons
 * @param item
 * @return
 */
function getList(buttons, item) {
    var data = buttons.get(item).attr("onclick");
    data = RegexUtils.GetText(data, "javascript:QueryListDetail\\((.*)\\);");
    data = data.replace(/\s*/g,"")
    //'01-05', '01-06','支付宝 -The Dairy Farm Company, Limited','￥1,190.61','7627','CN','01-05','￥1,190.61'
    var split = data.split("\',\'");
    var newResult = "[";
    //处理掉因为含有单引号引起的json解析异常
    for(var i = 0;i < split.length;i++){
        split[i] = "\'" + split[i].replaceAll("\\'","") + "\'";
        if(i<split.length-1){
            newResult = newResult + split[i] + ",";
        }else{
            newResult = newResult+split[i]+"]";
        }
    }
    print(newResult);
    var list = JSON.parseObject(newResult, List.class);
    return list;
}

/**
 * 把分期描述里的数字提取出来
 * @param des
 * @return
 */
function getFen(des) {
    var list = new ArrayList();
    var m =  Pattern.compile("\\d+").matcher(des);
    while (m.find()) {
        list.add(Integer.parseInt(m.group()));
    }
    return list;
}

/**
 * 12个月
 * @return
 */
function getRecentMonth() {
    var dates = new ArrayList();
    var sdf = new SimpleDateFormat("YYYY-MM");
    var cld = Calendar.getInstance();
    for (var i = 0; i < 12; i++) {
        var day = cld.get(Calendar.DAY_OF_MONTH);

        if (day <= Integer.parseInt(billDate)) {
            cld.add(Calendar.MONTH, -1);
            dates.add(sdf.format(cld.getTime()));
        } else {
            dates.add(sdf.format(cld.getTime()));
            cld.add(Calendar.MONTH, -1);
        }
    }
    return dates;
}

/**
 * 卡号后四位
 * @param cardNumList
 * @return
 */
function getCardNumList(cardNumList) {
    var sb = new StringBuffer();
    for (var i = 0; i < cardNumList.size(); i++) {
        sb.append(cardNumList.get(i).substring((cardNumList.get(i).length() - 4), (cardNumList.get(i).length()))).append(",");
    }
    return sb.toString().trim().substring(0, sb.toString().length - 1);
}

/**
 * 卡号集合
 * @param list
 * @return
 */
function list2String(list) {
    var sb = new StringBuffer();
    for (var i = 0; i < list.size(); i++) {
        sb.append(list.get(i)).append(",");
    }
    return sb.toString().trim().substring(0, sb.toString().length - 1);
}

function accountAndcardNoAndbilloutAndbillHeaders() {
    var headers = new HashMap();
    headers.put("User-Agent", params.USER_AGENT);
    headers.put("Accept", params.ACCEPT);
    headers.put("Host", "html.m.cmbchina.com");
    headers.put("Origin", "https://html.m.cmbchina.com");
    headers.put("Referer", "https://html.m.cmbchina.com/MobileHtml/Login/MsgVerify.aspx");
    headers.put("Accept-Encoding", "gzip, deflate, br");
    headers.put("Connection", "keep-alive");
    headers.put("Content-Type", "application/x-www-form-urlencoded");
    return headers;
}

/**
 * 获取登录页面
 * @returns {*}
 */
function getLoginHtml() {
    var xmlReq = context.get("xmlReq");
    var parametersMap = new HashMap();
    parametersMap.put("ClientNo", clientNo);
    parametersMap.put("Command", "CMD_DOLOGIN");
    parametersMap.put("XmlReq", xmlReq);

    var header = new HashMap();
    header.put("User-Agent", params.USER_AGENT);
    header.put("Accept", params.ACCEPT);
    header.put("Host", "html.m.cmbchina.com");
    header.put("Origin", "https://html.m.cmbchina.com");
    header.put("Referer", "https://html.m.cmbchina.com/MobileHtml/Login/LoginC.aspx");

    //var loginHtml = post(urls.LOGIN_C_URL, parametersMap, header);
    var loginHtml = cmbSubmitWithReTry(urls.LOGIN_C_URL,parametersMap,header,"登录","post")
    storeDataToFile(context, loginHtml, "loginSucc1.html");
    return loginHtml;
}

/**
 * 检查点击登录时的错误
 * @param html
 * @returns {boolean}
 */
/*function getLoginError(html) {
    if (loginSuccess(html)) {
        logger.info("{},不需要短信验证码", context.get(LUA_TASK_KEY));

        storeDataToFile(context, html, "loginSucc.html");
        // 登录成功，跳出本方法
        return true;
    } else if (needSms(html)) {
        logger.info("{},需要短信验证码", context.get(LUA_TASK_KEY));

        storeDataToFile(context, html, "needSms.html");
        needSmsFlag = true;
        return true;
    } else if (getLoginErrMsg(html).contains("未查询到相关客户记录") || getLoginErrMsg(html).contains("未查询到相关卡片记录，如需帮助可致电400-820-5555转接服务专员为您服务。")  || getLoginErrMsg(html).contains("无效卡号")) {
        logger.info("{},未查询到相关客户记录", context.get(LUA_TASK_KEY));

        storeDataToFile(context, html, "ZhangHuError.html");
        throw new BankException(BALO_22101_10, errorMsgMap.get(BALO_22101_10));
    } else if (getLoginErrMsg(html).contains("无效登录请求，可能是您超时未操作！请重新打开登录页面")) {
        logger.info("{},无效登录请求，可能是您超时未操作！请重新打开登录页面", context.get(LUA_TASK_KEY));

        storeDataToFile(context, html, "WuXiaoError.html");
        throw new BankException(BALO_99601_30, errorMsgMap.get(BALO_99601_30));
    } else if (getLoginErrMsg(html).contains("证件号码不能为空，请重新输入！") || getLoginErrMsg(html).contains("卡号不能为空")) {
        logger.info("{},证件号码不能为空，请重新输入！", context.get(LUA_TASK_KEY));

        storeDataToFile(context, html, "idCardEmpty.html");
        throw new BankException(BALO_22003_10, "证件号码不能为空，请重新输入！");
    } else if (getLoginErrMsg(html).contains("为了您的账户安全，设置密码")) {
        logger.info("{},为了您的账户安全，设置密码时不要用身份证、生日或连续简单的数字", context.get(LUA_TASK_KEY));

        storeDataToFile(context, html, "PwdEasyError.html");
        throw new BankException(BALO_22003_10, errorMsgMap.get(BALO_22003_10));
    } else if (getLoginErrMsg(html).contains("您的查询密码输入错误，请重新输入。")) {
        logger.info("{},您的查询密码输入有误，请重新输入。", context.get(LUA_TASK_KEY));

        storeDataToFile(context, html, "PwdError.html");
        throw new BankException(BALO_22001_10, errorMsgMap.get(BALO_22001_10));
    } else if (getLoginErrMsg(html).contains("系统繁忙，请稍后再试")) {
        storeDataToFile(context, html, "XiTongError.html");

        logger.info("{},系统繁忙，请稍后再试", context.get(LUA_TASK_KEY));
        throw new BankException(BALO_21301_10, errorMsgMap.get(BALO_21301_10));
    } else if (getLoginErrMsg(html).contains("错误次数已满")) {
        logger.info("{},错误次数已满", context.get(LUA_TASK_KEY));

        storeDataToFile(context, html, "ErrorCount.html");
        throw new BankException(BALO_22002_10, errorMsgMap.get(BALO_22002_10));
    } else if (getLoginErrMsg(html).contains("附加码不能为空,请重新输入!")) {
        logger.info("{},附加码不能为空,请重新输入!", context.get(LUA_TASK_KEY));

        storeDataToFile(context, html, "ImgError.html")
        return false;
    } else if (getAlertMsg(html).contains("为了您的帐户安全，设置密码时请不要选用您的身份证、生日、手机号码、卡号、重复或连续等简单的数字。")) {
        logger.info("{},为了您的帐户安全，设置密码时请不要选用您的身份证、生日、手机号码、卡号、重复或连续等简单的数字。", context.get(LUA_TASK_KEY));

        storeDataToFile(context, html, "pwd.html")
        throw new BankException(BALO_22003_10, errorMsgMap.get(BALO_22003_10));
    } else if (html.contains("亲爱的准信用卡客户，请您登录掌上生活客户端进行相关业务的查询与办理") || html.contains("Object moved to here") || html.contains("信用卡开卡")) {
        logger.info("{},您的卡片未激活，请先激活", context.get(LUA_TASK_KEY));
        storeDataToFile(context, html, "Error.html")
        throw new BankException(BALO_22103_10, errorMsgMap.get(BALO_22103_10));
    } else if (getLoginErrMsg(html).contains("无效附加码")) {
        logger.info("{},无效附加码", context.get(LUA_TASK_KEY));
        return getLoginError(getLoginHtml());
    } else if (html.contains("Access Denied")) { //代理IP异常
        throw new BankException(BALO_99601_10, errorMsgMap.get(BALO_99601_10));
    } else if (html.contains("频繁操作") || getLoginErrMsg(html).contains("系统繁忙，请稍候再试！")) {
        throw new BankException(BACR_21301_10, errorMsgMap.get(BACR_21301_10));

    } else if (html.contains("Object moved to ")) {
        throw new BankException(BALO_20304_30, errorMsgMap.get(BALO_20304_30));

    } else if (getLoginErrMsg(html).contains("系统暂时无法处理您的请求，如需帮助可致电400-820-5555转接服务专员为您服务。")) {
        throw  new BankException(BALO_22107_10, getLoginErrMsg(html));

    } else if (getLoginErrMsg(html).contains("您的信用片已过有效期，暂时无法处理您的请求。")) {
        throw new BankException(BALO_99602_30, getLoginErrMsg(html));

    } else if (getLoginErrMsg(html).contains("附属卡无法登录，如您为主卡持卡人，请使用您名下主卡卡号进行登录，谢谢。")) {
        throw new BankException(BALO_22203_10, getLoginErrMsg(html));

    }else if(getLoginErrMsg(html).contains("请登录招行APP设置允许[大众版登录]")) {
        throw new BankException(BALO_22102_10, errorMsgMap.get(BALO_22102_10));

    } else if(getLoginErrMsg(html).contains("不能在当前渠道使用，如需使用，请登录“网上银行专业版”在“专业版管理”中重新开通使用。")){
        throw new BankException(BALO_99603_45, getLoginErrMsg(html));

    } else {
        logger.info("{} 未知类型的错误{}，页面保存到loginError", context.get(LUA_TASK_KEY), getLoginErrMsg(html));
        storeDataToFile(context, getLoginErrMsg(html), "loginError.html")
        // 账户错误抛异常
        throw new BankException(BALO_23001_10, getLoginErrMsg(html));
    }
}*/

/**
 * 判断是否登录成功
 *
 * @param html
 * @return
 */
function loginSuccess(html) {
    return StringUtils.contains(html, SUCCESS_TAG);
}

/**
 * 判断是否需要短信验证码
 *
 * @param html
 * @return
 */
function needSms(html) {
    return StringUtils.contains(html, SMS_TAG);
}

// 取得登录错误消息
function getLoginErrMsg(html) {
    return RegexUtils.GetText(html, "errMsg = \"(.*)\";");
}

/**
 * 没有errMsg的内容
 *
 * @param html
 * @return
 */
function getAlertMsg(html) {
    return RegexUtils.GetText(html, "alert\\(\"(.*)\"\\);");
}

// 取得短信验证码消息
function getSmsErrMsg(html) {
    return Jsoup.parse(html).select("#TitleMsg").text();
}

//带有重试的方法，在catch到ssl异常的时候进行重试,重试次数，url,参数，头，方法描述
function cmbSubmitWithReTry(url,params,header,desc,type){
    var html = "";
    var tryTimes = 0;
    var totalCounts = 3;
    do{
        tryTimes++;
        logger.info("taskkey={},"+desc+"抓取正在进行第"+tryTimes+"次尝试..",context.get(LUA_TASK_KEY));
        try{
            //休眠半秒
            Thread.sleep(500);
            if(type.equals("post")){
                html = post(url, params, header);
            }else{
                html = get(url);
            }
            if(tryTimes>5){
                break;
            }
            //如果提示操作频繁，休眠十秒然后继续操作
            if(html.contains("请不要频繁操作，稍候片刻再返回菜单继续使用")){
                logger.info("taskkey={},网站返回操作频繁，等待进行重试..",context.get(LUA_TASK_KEY))
                Thread.sleep(5000);
                totalCounts++;
                continue;
            }
            if(StringUtils.isNotBlank(html) && !html.contains("请不要频繁操作，稍候片刻再返回菜单继续使用")){
                logger.info("taskKey={},"+desc+"抓取第"+tryTimes+"次尝试成功..",context.get(LUA_TASK_KEY));
                break;
            }
        }catch (e){
            // html.m.cmbchina.com:443 failed to respond
            if(e.message.contains("443 failed to respond")){
                logger.error("taskKey={} " + desc + "抓取异常443：{}", context.get(LUA_TASK_KEY), e);
                continue;
            }
            if(!(e.message.contains("Remote host closed connection during handshake") || e.message.contains("SSL peer shut down incorrectly")) || tryTimes>=3){
                logger.error("taskKey={} " + desc + "抓取异常：{}", context.get(LUA_TASK_KEY), e);
                statCaughtException(context, e);
                break;
            }
            logger.info("taskKey={},"+desc+"抓取请求发生ssl异常，准备重试..",context.get(LUA_TASK_KEY));
        }
    }while(tryTimes < totalCounts);
    return html;
}
function getTheTransDate(description,recordDate){
    if(description.split("-").length >=1){
        var installment_str = description.split("-")[0];
        var installment_num = Number(getNum(installment_str));
        var calendar2 = Calendar.getInstance();
        var the_year = calendar2.get(Calendar.YEAR);
        return AddMouth(recordDate,installment_num-1,"sub");
    }else{
        logger.info("日期获取出错")
        return recordDate
    }
}

function getNum(text){
    var value = text.replace(/[^0-9]/ig,"");
    return value
}

/**
 * 增加获取减去某个月份，增加暂时不完善
 * @param date
 * @param num
 * @returns {string}
 * @constructor
 */
function AddMouth(date, num,type)
{
    if(type == "add"){
        var mouthnum = parseInt(num);
        var year = parseInt(date.substring(0, 4));
        var mouth = parseInt(date.substring(5, 7));
        var day = parseInt(date.substring(8, 10));
        if (mouth + mouthnum > 12)
        {
            var newyear = year + 1;
            var newmouth = mouth + mouthnum - 12;
            var newday = day;
        }
        else
        {
            var newyear = year
            var newmouth = mouth + mouthnum;
            var newday = day;
        }
        var newdate = newyear + "-" + newmouth + "-" + newday;
        return newdate;
    }else{
        var mouthnum = parseInt(num);
        var year = parseInt(date.substring(0, 4));
        var mouth = parseInt(date.substring(5, 7));
        var day = parseInt(date.substring(8, 10));
        if (mouth - mouthnum < 0)
        {
            var num1 = Math.floor((mouthnum-mouth)/12)+1
            if(num1<=1){
                var newyear = year - 1;
                var newmouth = 0
                if(mouth + mouthnum - 12 > 0){
                    newmouth = (mouth - mouthnum) + 12;
                }else{
                    newmouth = 12 - (mouthnum-mouth);
                }
            }else{
                mouthnum = mouthnum % 12
                var newyear = year - num1;
                if(mouth - mouthnum <= 0){
                    var newmouth = 0
                    if(mouth + mouthnum - 12 > 0){
                        newmouth = (mouth - mouthnum) + 12;
                    }else{
                        newmouth = 12 - (mouthnum-mouth);
                    }
                }else{
                    newmouth = mouth-mouthnum
                }
            }
            var newday = day;
        }
        else
        {
            var newyear = year
            var newmouth = mouth - mouthnum;
            var newday = day;
        }
        var newdate = newyear + "-" + newmouth + "-" + newday;
        return newdate;
    }
}